package org.budo.graph.druid.filter.connection;

import java.sql.Connection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.budo.support.lang.util.MapUtil;
import org.budo.support.slf4j.Slf4j;
import org.budo.time.Time;
import org.slf4j.Logger;

import com.alibaba.druid.pool.DruidPooledConnection;
import com.alibaba.druid.support.profile.ProfileEntryKey;
import com.alibaba.druid.support.profile.ProfileEntryReqStat;
import com.alibaba.druid.support.profile.Profiler;

/**
 * @author limingwei
 */
public class ActiveConnectionFilterUtil {
    private static final Logger log = Slf4j.getLogger();

    /**
     * getCurrentProfilerList
     */
    public static List<Object> getCurrentProfilerList() {
        List<Object> profilerList = new ArrayList<Object>();

        List<Object> activeConnectionProfilerList = ActiveConnectionProfiler.getProfilerListCopy();
        if (null != activeConnectionProfilerList && !activeConnectionProfilerList.isEmpty()) { // 新的数据
            profilerList.addAll(activeConnectionProfilerList);
        }

        Map<ProfileEntryKey, ProfileEntryReqStat> statsMap = Profiler.getStatsMap();
        if (null != statsMap) { // 原来的数据
            Map<ProfileEntryKey, ProfileEntryReqStat> statsMapCopy = new HashMap<ProfileEntryKey, ProfileEntryReqStat>(statsMap);
            profilerList.add(statsMapCopy);
        }
        return profilerList;
    }

    public static String activeConnectionsToHtml(Map<Connection, Object> activeConnections) {
        if (null == activeConnections || activeConnections.isEmpty()) {
            return "activeConnections is null or empty";
        }

        String result = "";
        Set<Entry<Connection, Object>> entrySet = activeConnections.entrySet();
        int index = 0;
        for (Entry<Connection, Object> entry : entrySet) {
            result += "ActiveConnection-" + (index++) + " = " + entry.getKey() + ", Type = " + (entry.getKey().getClass().getName()) + "<br/>";

            result += connectionToHtml(entry.getKey()) + "<br/>";
            result += statsMapToHtml(entry.getValue()) + "<br/><br/>";
        }
        return result;
    }

    /**
     * DruidPooledConnection
     */
    private static String connectionToHtml(Connection connection) {
        try {
            if (null == connection || !(connection instanceof DruidPooledConnection)) {
                return "connection is null or is not DruidPooledConnection, connection=" + connection;
            }

            DruidPooledConnection druidPooledConnection = (DruidPooledConnection) connection;
            String result = "traceEnable = " + druidPooledConnection.isTraceEnable() + ", ";
            result += "disable = " + druidPooledConnection.isDisable() + ", ";
            result += "closed = " + druidPooledConnection.isClosed() + ", ";
            result += "abandoned = " + druidPooledConnection.isAbandonded() + ", ";
            result += "ownerThread = " + druidPooledConnection.getOwnerThread() + "<br/>";

            result += "connectedTimeNano = " + druidPooledConnection.getConnectedTimeNano() + ", ";
            long connectedTimeMillis = druidPooledConnection.getConnectedTimeMillis();
            result += "connectedTimeMillis = " + connectedTimeMillis + " = " + Time.when(connectedTimeMillis).toString("yyyy-MM-dd HH:mm:ss SSS") + "<br/>";
            result += "connectStackTrace = " + stackTraceElementsToHtml(druidPooledConnection.getConnectStackTrace());
            return result;
        } catch (Throwable e) {
            log.error("connectionToHtml error, " + e, e);
            return "connectionToHtml error, " + e;
        }
    }

    private static String stackTraceElementsToHtml(StackTraceElement[] stackTraceElements) {
        if (null == stackTraceElements || stackTraceElements.length < 1) {
            return "stackTraceElements is null or empty, " + stackTraceElements;
        }

        String result = "";
        for (StackTraceElement element : stackTraceElements) {
            result += element.getClassName() + "." + element.getMethodName() + "() " + element.getFileName() + " #" + element.getLineNumber() + "<br/>";
        }
        return result;
    }

    /**
     * 可能是 List<Object> 类型
     */
    private static String statsMapToHtml(Object stats) {
        if (null == stats) {
            return "stats is null, " + stats;
        } else if (stats instanceof List) {
            List<Map<String, Object>> resultMapList = new ArrayList<Map<String, Object>>();

            for (Object each : (List) stats) {
                if (null == each) {
                    resultMapList.add(MapUtil.stringObjectMap("name", "it,s null"));
                } else if (each instanceof String) { // @see ProviderSideDruidWebStatFilterToDubboFilter.invoke(Invoker<?>, Invocation)
                    resultMapList.add(MapUtil.stringObjectMap("name", each));
                } else if (each instanceof Map) {
                    Set entrySet = ((Map) each).entrySet();
                    for (Object entryeach : entrySet) { // @see ActiveConnectionFilter.dataSource_getConnection(FilterChain, DruidDataSource, long)
                        Entry entry = (Entry) entryeach;
                        ProfileEntryKey profileEntryKey = (ProfileEntryKey) entry.getKey();
                        ProfileEntryReqStat profileEntryReqStat = (ProfileEntryReqStat) entry.getValue();

                        resultMapList.add(MapUtil.stringObjectMap("name", profileEntryKey.getName(), //
                                "parentName", profileEntryKey.getParentName(), //
                                "type", profileEntryKey.getType(), //
                                "executeCount", profileEntryReqStat.getExecuteCount(), //
                                "executeTimeNanos", profileEntryReqStat.getExecuteTimeNanos()));
                    }
                } else {
                    resultMapList.add(MapUtil.stringObjectMap("name", "type is " + each.getClass().getName()));
                }
            }

            return resultMapListToHtml(resultMapList);
        } else {
            return "stats type error, " + stats.getClass() + ", " + stats;
        }
    }

    private static String resultMapListToHtml(List<Map<String, Object>> resultMapList) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("<table>");
        stringBuffer.append("<tr>");
        stringBuffer.append("<td>N</td>");
        stringBuffer.append("<td>Name</td>");
        stringBuffer.append("<td>ParentName</td>");
        stringBuffer.append("<td>Type</td>");
        stringBuffer.append("<td>ExecuteCount</td>");
        stringBuffer.append("<td>ExecuteTimeNanos</td>");
        stringBuffer.append("</tr>");

        int index = 0;
        for (Map<String, Object> each : resultMapList) {
            stringBuffer.append("<tr>");
            stringBuffer.append("<td>" + (index++) + "</td>");
            stringBuffer.append("<td>" + each.get("name") + "</td>");
            stringBuffer.append("<td>" + each.get("parentName") + "</td>");
            stringBuffer.append("<td>" + each.get("type") + "</td>");
            stringBuffer.append("<td>" + each.get("executeCount") + "</td>");
            stringBuffer.append("<td>" + each.get("executeTimeNanos") + "</td>");
            stringBuffer.append("</tr>");
        }

        return stringBuffer.toString();
    }
}